package com.qiwenshare.file.file_encryption;
import java.io.*;
import java.util.Base64;


/**
 * 实例化该类，生成文件加密对象，
 * 调用生成对象的encrypt方法完成加密，
 * 调用decrypt方法完成解密，
 * 调用getSM4Key获取16字节的SM4文件加解密密钥。
 * @author 成琪
 * @version 1.0
 * @date 2022-01-05 17:04
 */
public class SM4FileEncryption {


    private static String algorithm="SM4";
    private static String version="1";
    private static byte[] key=new byte[16];
    //private static String encryptFile=null;
    //设置800M缓冲区，可更改，缓冲区越小，读取文件速度越慢
    private static int BufferSize=2048*1024;

    /**
     * 构造对象
     * @param encryptFile 加密文件夹的绝对路径
     */
    //public SM4FileEncryption(String encryptFile)  {
    //    SM4FileEncryption.encryptFile =encryptFile;
    //}


    /**
     * @return 返回文件加密密钥
     */

    public static byte[] getSM4Key() {
        return key;
    }


    /**
     * 加密文件
     * @param srcFilePath 待加密文件路径
     * @throws Exception 抛出异常
     */

    public void encrypt(String srcFilePath) throws Exception {
        //定义密文文件输出路径
        //cipFilePath=加密文件夹路径+原文件名
        //String cipFilePath=SM4FileEncryption.encryptFile+Utils.getFileName(srcFilePath)+".sm4";
        String cipFilePath=(srcFilePath)+".sm4";
        System.out.println("cipFile="+cipFilePath);
        //初始化iv值
        // iv对加密结果进行随机化。在使用同一种加密同一个密钥时,密文也不一致
        byte [] iv = "iviviviviviviviv".getBytes();//待修改
        //生成密钥
        key = SM4Utilis.generateKey(srcFilePath);
        //System.out.println(Base64.getEncoder().encodeToString(key));

        //这里开始加入文件头部
        BufferedOutputStream cipOutputStream = new BufferedOutputStream(new FileOutputStream(cipFilePath,true));
        //头部添加算法名（3字节）：SM4
        cipOutputStream.write("SM4".getBytes());
        //头部添加版本号（2字节）：01
        cipOutputStream.write("01".getBytes());
        //头部添加初始iv(16字节)：iv
        cipOutputStream.write(iv);
        /*byte[] yunQian=("aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa" +
                "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa" +
                "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa" +
                "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa").getBytes();*/
        byte[] yunQian=("MjEzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMjE5amY4bmhucGFzYXNmYX" +
                "NmcyV1NTU0QSV1NTU0QXIldTdCOTcldTZDRDVzZnMlMjBmYXMldTUyMDZhJXU3Qjk3JXU2Q0" +
                "Q1ZnNYldDZzZnNmJXU5NjNGJXU4NDI4JXU2Q0Q1pNek16TXpNk16TXpNek16TXpNek16TXpN16TXpNak" +
                "U1YW1ZNGJtaHVjR0Z6WVhObVlYTm1jeVYxTlQ=").getBytes();
        cipOutputStream.write(yunQian);
        System.out.println("head add success");


        //加密明文部分
        int srcRead=BufferSize;
        //缓冲区大小
        byte[] srcByte =new byte[BufferSize];
        byte[] cipByte =null;
        long cipStart =System.currentTimeMillis();
        BufferedInputStream srcInputStream = new BufferedInputStream(new FileInputStream(srcFilePath));
        while((srcRead=srcInputStream.read(srcByte))%BufferSize==0){
            //System.out.println(srcRead);
            //System.out.println(Utils.bytesToHex(srcByte)+" "+srcByte.length);
            cipByte = SM4Utilis.encrypt("SM4/CTR/NoPadding",key,iv,srcByte);
            cipOutputStream.write(cipByte);
            //System.out.println(cipByte.length);
        }

        srcInputStream.close();
        File file = new File(srcFilePath);
        file.delete();

        //读取最后一行,排除空文件
        if(srcRead!=-1){
            //System.out.println("===========================================");
            //System.out.println(srcRead);
            byte[] tmp = new byte[srcRead];
            for(int i=0;i<srcRead;i++){
                tmp[i]=srcByte[i];
            }
            //System.out.println(Utils.bytesToHex(tmp)+" "+tmp.length);
            cipByte = SM4Utilis.encrypt("SM4/CTR/NoPadding",key,iv,tmp);
            cipOutputStream.write(cipByte);
        }




        long cipEnd =System.currentTimeMillis();
        System.out.println("data encrypt success");
        System.out.println("encrypt time: "+(cipEnd-cipStart)+" ms");

        //尾部HMAC部分
        //System.out.println("HMAC-SM3 success");
        cipOutputStream.close();
    }

    /**
     * 解密文件
     * @param cipFilePath 加密完的文件保存路径
     * @param desFilePath 解密文件保存路径
     * @throws Exception
     */

    public void decrypt(String cipFilePath,String desFilePath) throws Exception{
        System.out.println("-------------------------------------------");
        //定义读取加密文件流
        BufferedInputStream cipInputStream = new BufferedInputStream(new FileInputStream(cipFilePath));

        //开始解析
        //解析头部得到加密方式（3字节）
        byte[] alg=new byte[3];
        cipInputStream.read(alg);
        System.out.println("decrypt mode :"+Utils.getString(alg));

        //解析获得版本号(2字节)
        byte[] version=new byte[2];
        cipInputStream.read(version);
        System.out.println("encrypt version :"+Utils.getString(version));

        //解析获得初始iv（16字节）
        byte[] iv=new byte[16];
        cipInputStream.read(iv);
        System.out.println("init iv:"+Utils.getString(iv));

        //解析获得云签加密后的密文（256字节）
        byte[] yunQian=new byte[256];
        cipInputStream.read(yunQian);
        //System.out.println("SM2-YunQian :"+Utils.getString(yunQian));

        //调用云签接口，获取文件加密密钥
        byte[] key=SM4FileEncryption.getSM4Key();//待修改
        System.out.println("get file_encrypt_key");

        //解密密文
        int cipRead=0;
        //缓冲区大小
        byte[] cipByte =new byte[BufferSize];
        byte[] decByte =null;
        long desStart =System.currentTimeMillis();
        //解密文件输出流
        BufferedOutputStream desOutputStream = new BufferedOutputStream(new FileOutputStream(desFilePath,true));
        while((cipRead=cipInputStream.read(cipByte))%BufferSize==0){
            //System.out.println(cipRead);
            //System.out.println(Utils.bytesToHex(cipByte)+" "+cipByte.length);
            decByte = SM4Utilis.decrypt("SM4/CTR/NoPadding",key,iv,cipByte);
            desOutputStream.write(decByte);
        }

        cipInputStream.close();
        File file = new File(cipFilePath);
        file.delete();

        //读取最后一行,排除空文件
        if(cipRead!=-1){
            //System.out.println("++++++++++++++++++++++++++++++++++++++++++++");
            //System.out.println(cipRead);
            byte[] tmp = new byte[cipRead];
            for(int i=0;i<cipRead;i++){
                tmp[i]=cipByte[i];
            }
            //System.out.println(Utils.bytesToHex(tmp)+" "+tmp.length);
            decByte = SM4Utilis.decrypt("SM4/CTR/NoPadding",key,iv,tmp);
            desOutputStream.write(decByte);
        }


        //关闭流
        desOutputStream.close();

        long desEnd =System.currentTimeMillis();
        System.out.println("decrypt path : "+ desFilePath);
        System.out.println("decrypt success");
        System.out.println("decrypt time : "+(desEnd-desStart)+" ms");
    }


}